package com.power.sifa.approve.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.aventrix.jnanoid.jnanoid.NanoIdUtils;
import com.plf.common.domain.ResponseDTO;
import com.plf.common.domain.YesNo;
import com.plf.common.utils.CommonUtils;
import com.plf.common.utils.PdfConvertUtil;
import com.plf.common.utils.StringUtil;
import com.plf.core.base.dao.Ha_attachmentDao;
import com.plf.core.base.model.Ha_attachment;
import com.plf.core.base.service.Ha_attachmentService;
import com.plf.core.common.service.BaseServiceImpl;
import com.power.flowable.api.IPersonalFeign;
import com.power.sifa.approve.dao.Apl_dossierDao;
import com.power.sifa.approve.model.*;
import com.power.sifa.approve.model.enumeration.*;
import com.power.sifa.approve.model.pageInfo.Apl_dossierPageInfo;
import com.power.sifa.approve.model.pojo.Term;
import com.power.sifa.approve.model.pojo.Volume;
import com.power.sifa.approve.service.*;
import com.power.sifa.std.dao.Std_matter_attachDao;
import com.power.sifa.supervise.model.Apl_feedbackdoc;
import com.power.sifa.supervise.service.Apl_feedbackdocService;
import com.power.sifa.util.CacheHelper;
import com.power.sifa.util.RemoteFileUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.springframework.beans.BeanUtils;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;

/**
 * 电子卷宗业务层实现
 */
@Service
@Slf4j
public class Apl_dossierServiceImpl extends BaseServiceImpl implements Apl_dossierService {

//    @Resource
//    private Apl_dossierService service;
    @Resource
    private Apl_dossierDao dao;
    @Resource
    private Ha_attachmentDao ha_attachmentDao;
    @Resource
    private Apl_dossierDao apl_dossierDao;
    @Resource
    private Std_matter_attachDao std_matter_attachDao;
    @Resource
    private Aprv_process_materialsService aprv_process_materialsService;
    @Resource
    private BusinessApproveService businessApproveService;
    @Resource
    private Ha_attachmentService ha_attachmentService;
    @Resource
    private CacheHelper cacheHelper;
    @Resource
    private Apl_archiveService apl_archiveService;
    @Resource
    private Apl_admreviewService apl_admreviewService;
    @Resource
    private Apl_mainService apl_mainService;
    @Resource
    private IPersonalFeign iPersonalFeign;

    @Resource
    private Apl_matter_attachService apl_matter_attachService;

    @Resource
    private Apl_feedbackdocService apl_feedbackdocService;

    @Override
    public Apl_dossier getById(Serializable id) {
        return dao.findById(id).orElse(null);
    }

    /**
     * 线程池
     */
    public ExecutorService threadPool = new ThreadPoolExecutor(12, 24, 2, TimeUnit.MILLISECONDS,
            new LinkedBlockingQueue<Runnable>());


    @Transactional(rollbackFor = Exception.class)
    @Override
    public void saveOrUpdate(Apl_dossier apl_dossier) {
        // save 如果数据库不存在这条数据，就会执行保存（即将这条数据插入到数据库）；如果数据库存在这条数据，就会根据主键去更新这条数据
        if (apl_dossier.getPrimaryKey() != null) {
            Apl_dossier apl_dossierfDb = dao.findById(apl_dossier.getPrimaryKey()).orElse(null);
            if (apl_dossierfDb != null) {
                BeanUtils.copyProperties(apl_dossier, apl_dossierfDb);
                dao.save(apl_dossierfDb);
                return;
            }
        }
        dao.save(apl_dossier);
    }

    @Override
    public void removeById(Serializable id) {
        dao.deleteById(id);
    }

    @Override
    public Page<Apl_dossier> findAll(Apl_dossierPageInfo pageInfo) {
        //排序等定义
        Sort sort;
        if ("asc".equals(pageInfo.getOrder())) {
            sort = Sort.by(Sort.Direction.ASC, pageInfo.getSort());
        } else {
            sort = Sort.by(Sort.Direction.DESC, pageInfo.getSort());
        }
        //分页的定义
        Pageable pageable = PageRequest.of(pageInfo.getPageNumber() <= 1 ? 0 : pageInfo.getPageNumber() - 1, pageInfo.getPageSize(), sort);
        //查询条件
        Specification spec = this.createSpecification(pageInfo);
        return dao.findAll(spec, pageable);
    }

    @Transactional(rollbackFor = Exception.class)
    public void removeByIds(List<Integer> ids) {
        dao.removeByIds(ids);
    }

    @Override
    public List<Apl_dossier> findAll() {
        return dao.findAll();
    }

    @Override
    public void saveAll(List<Apl_dossier> list) {
        dao.saveAll(list);
    }

    /**
     * 通过applyid查询对应的电子卷宗文件树形结构
     *
     * @param applyid applyid
     * @return
     */
    @Override
    public List<Apl_dossier> getDocTreeListByApplyid(String applyid) {
        Specification<Apl_dossier> specification = (root, query, criteriaBuilder) -> query.where(
                criteriaBuilder.equal(root.get("applyid"), applyid)
        ).getRestriction();
        Sort sort = Sort.by(Sort.Direction.ASC, "sort");
        List<Apl_dossier> dossierList = dao.findAll(specification, sort);

        if (CollectionUtil.isNotEmpty(dossierList)) {
            // 遍历全部的材料的id
            // materialIds保存的就是附件id
            List<String> materialIds = dossierList.stream().filter(item -> item.getType().equals(Tp_dostype.FILE))
                    .map(Apl_dossier::getMatid).collect(Collectors.toList());
            if (CollectionUtil.isNotEmpty(materialIds)) {
                // 获取材料文件
                Map<String, Ha_attachment> ha_attachmentMap = ha_attachmentService.getHa_attachmentsByIds(materialIds);
                dossierList.stream().filter(item -> item.getType().equals(Tp_dostype.FILE)).forEach(item -> {
                    Ha_attachment fileMent = ha_attachmentMap.get(item.getMatid());
                    if (ObjectUtil.isNotNull(fileMent)) {
                        item.setName(fileMent.getFilename());
                        item.setFile(fileMent);
                    }
                });
            }
        }
        return dossierList;
    }

    @Override
    public List<Apl_dossier> getDocTreeListByParentId(String parentId) {
        Specification<Apl_dossier> specification = (root, query, criteriaBuilder) -> query.where(
                criteriaBuilder.equal(root.get("parentid"), parentId)
        ).getRestriction();
        List<Apl_dossier> dossierList = dao.findAll(specification);
        return dossierList;
    }

    /**
     * 删除树层级节点
     *
     * @param treeNodeId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteTreeNode(String treeNodeId) {
        this.removeById(treeNodeId);
        List<Apl_dossier> child = this.getDocTreeListByParentId(treeNodeId);
        if (CollectionUtil.isNotEmpty(child)) {
            child.stream().forEach(item -> {
                this.deleteTreeNode(item.getId());
            });
        }
    }

    /**
     * 新增树层级节点
     *
     * @param treeNodeType
     * @param treeNodeName
     * @param parentId
     * @throws Exception
     */
    @Override
    public Apl_dossier addTreeNode(Integer treeNodeType, String treeNodeName, String parentId) throws Exception {
        // 获取父节点数据
        Apl_dossier apl_dossier = this.getById(parentId);
        if (ObjectUtil.isNull(apl_dossier)) {
            throw new Exception("父节点为空");
        }
        String applyid = apl_dossier.getApplyid();
        List<Apl_dossier> dossierList = this.getDocTreeListByParentId(parentId);
        // 获取最大的sort
        Apl_dossier maxDossier = dossierList.stream().max(Comparator.comparingInt(Apl_dossier::getSort)).orElse(null);
        Integer sort = ObjectUtil.isNotNull(maxDossier) ? maxDossier.getSort() + 1 : 1;
        Apl_dossier treeNodeData = Apl_dossier.builder().id(applyid + NanoIdUtils.randomNanoId()).applyid(applyid).name(treeNodeName)
                .type(treeNodeType).sort(sort).deleted(YesNo.YES).parentid(apl_dossier.getId()).build();
        this.saveOrUpdate(treeNodeData);
        this.inertTreeVal(treeNodeData);
        return treeNodeData;
    }

    /**
     * 根节点保存
     *
     * @param applyid
     * @param treeNodeName
     * @throws Exception
     */
    @Override
    public Apl_dossier addRootTreeNode(String applyid, String treeNodeName, String parentId) throws Exception {
        // 获取当前applyid的全部根节点数据
        Specification<Apl_dossier> specification = (root, query, criteriaBuilder) -> query.where(
                criteriaBuilder.and(criteriaBuilder.equal(root.get("applyid"), applyid),
                        criteriaBuilder.equal(root.get("type"), Tp_dostype.VOLUME))
        ).getRestriction();
        List<Apl_dossier> dossierList = dao.findAll(specification);
        Apl_dossier maxDossier = dossierList.stream().max(Comparator.comparingInt(Apl_dossier::getSort)).orElse(null);
        Integer sort = ObjectUtil.isNotNull(maxDossier) ? maxDossier.getSort() + 1 : 1;
        Apl_dossier treeNodeData = Apl_dossier.builder().id(applyid + NanoIdUtils.randomNanoId()).applyid(applyid).name(treeNodeName)
                .type(Tp_dostype.VOLUME).sort(sort).deleted(YesNo.YES).parentid(parentId).build();
        this.saveOrUpdate(treeNodeData);
        this.inertTreeVal(treeNodeData);
        return treeNodeData;
    }


    /**
     * 保存对应的文件到树层级
     *
     * @param applyid
     * @param parentId
     * @param materialId
     * @param materialName
     * @param deleted
     * @param mattype
     * @return
     * @throws Exception
     */
    @Override
    public Apl_dossier saveMaterialToTree(String applyid, String parentId, String materialId, String materialName, Integer deleted, String mattype) throws Exception {
        // 获取当前applyid的全部根节点数据
        Specification<Apl_dossier> specification = (root, query, criteriaBuilder) -> query.where(
                criteriaBuilder.and(criteriaBuilder.and(criteriaBuilder.equal(root.get("applyid"), applyid),
                        criteriaBuilder.equal(root.get("parentid"), parentId)),
                        criteriaBuilder.equal(root.get("matid"), materialId))
        ).getRestriction();
        List<Apl_dossier> dossierList = dao.findAll(specification);
        if (CollectionUtil.isNotEmpty(dossierList)) {
            throw new Exception("已存在同名文件");
        }
        List<Apl_dossier> dossierAllList = this.getDocTreeListByParentId(parentId);
        // 获取最大的sort
        Apl_dossier maxDossier = dossierAllList.stream().max(Comparator.comparingInt(Apl_dossier::getSort)).orElse(null);
        Integer sort = ObjectUtil.isNotNull(maxDossier) ? maxDossier.getSort() + 1 : 1;
        Apl_dossier treeNodeData = Apl_dossier.builder().id(applyid + NanoIdUtils.randomNanoId()).applyid(applyid).name(materialName)
                .type(Tp_dostype.FILE).sort(sort).matid(materialId).deleted(ObjectUtil.isNotNull(deleted) ? deleted :
                        YesNo.YES).parentid(parentId).mattype(StringUtil.isEmpty(mattype) ? null : mattype).build();
        this.saveOrUpdate(treeNodeData);
        Map<String, Ha_attachment> ha_attachmentMap = ha_attachmentService.getHa_attachmentsByIds(Arrays.asList(treeNodeData.getMatid()));
        if (ObjectUtil.isNotNull(ha_attachmentMap)) {
            treeNodeData.setFile(ha_attachmentMap.get(treeNodeData.getMatid()));
        }
        this.inertTreeVal(treeNodeData);
        return treeNodeData;
    }

    /**
     * 更新节点名称
     *
     * @param treeNodeId
     * @param treeNodeName
     */
    @Override
    public Apl_dossier updateTreeNodeName(String treeNodeId, String treeNodeName) {
        Apl_dossier apl_dossier = this.getById(treeNodeId);
        apl_dossier.setName(treeNodeName);
        this.saveOrUpdate(apl_dossier);
        this.inertTreeVal(apl_dossier);
        return apl_dossier;
    }

    /**
     * 初始化电子卷宗数据（行政复议）
     * @param applyid
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void buildInitDossier(String applyid) {
        // 1. 查询已有的过程材料
        Map<String, List<Aprv_process_materials>> matMap = this.queryMaterial(applyid);

        // 2. 构建初始化电子卷宗数据结构
        List<Apl_dossier> result = new ArrayList<>(40);
        // 2.1 获取正卷
        List<Volume> volList = new Volume().getList();
        // 2.2 获取正卷下的材料项
        List<Term> termList = new Term().getList();
        int i = 0;
        for (Volume volume : volList) {
            // 2.3 构造一个正卷
            Apl_dossier dosVolume = new Apl_dossier().BuilderVolume(applyid, volume.getName(), i++);
            result.add(dosVolume);
            // 默认的材料项加到第一个正卷下
            if (i == 1) {
                List<Apl_dossier> buildList = this.buildTrem(dosVolume, termList, matMap, St_servicetType.REVIEW);
                result.addAll(buildList);
            }

        }
        // 删除之前环节生成的卷宗表数据，删除完成后再插入新的数据
        this.dao.removeByApplyid(applyid);
        this.saveAll(result);
        log.info("保存文件{}", result);

        // 3. 更新目录页码结构
        this.updateTreeIndex(applyid);
        // 4.更新的归档主记录的电子卷宗卷数和册数
        this.updateMainRecordIndex(applyid, result);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDossier(String applyid, Integer serviceType) {
        // 需要更新的记录
        List<Apl_dossier> updateList = new ArrayList<>(40);

        // 1. 查询已有的过程材料
        Map<String, List<Aprv_process_materials>> matMap = this.queryMaterial(applyid);
        // 2. 查询已有的数据
        List<Apl_dossier> dosList = this.apl_dossierDao.getByApplyid(applyid);
        if (!CollectionUtils.isEmpty(dosList)) {
            List<String> matIds = new ArrayList<>(40);
            // 2.3 取出已经添加了的材料项
            for (Apl_dossier v : dosList) {
                matIds.add(v.getMatid());
            }
            // 2.4 查找更新
            for (Apl_dossier dosTerm : dosList) {
                if (Tp_dostype.TERM == dosTerm.getType() && null != dosTerm.getMattype()) {
                    String[] mattypeIds = dosTerm.getMattype().split(",");
                    for (String id : mattypeIds) {
                        // 如果是卷首, 卷目录, 卷尾调用一下生成材料的方法
                        if (id.equals(Tp_mattype.COVER + "")) {
                            // 删除之前生成的
                            this.dao.removeByParentid(dosTerm.getId());
                            Ha_attachment file = null;
                            try {
                                file = this.businessApproveService.createArchiveCover(applyid, serviceType);
                            } catch (Exception e) {
                                log.error("生成卷首文件失败", e);
                                throw new RuntimeException("生成卷首文件失败");
                            }
                            // 3. 生成一个文件项 - 卷首
                            Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, 0);
                            updateList.add(dosFile);
                            continue;
                        } else if (id.equals(Tp_mattype.DIRECTORY + "")) {
                            // 删除之前生成的
                            this.dao.removeByParentid(dosTerm.getId());
                            Ha_attachment file = this.businessApproveService.createArchiveDirectory(applyid, serviceType);
                            // 3. 生成一个文件项 - 卷目录
                            Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, 0);
                            updateList.add(dosFile);
                            continue;
                        } else if (id.equals(Tp_mattype.FILINGS + "")) {
                            // 4. 生成一个文件项 - 行政复议案件备考表（封底）
                            this.dao.removeByParentid(dosTerm.getId());
                            Ha_attachment file = this.businessApproveService.createArchiveBackCover(applyid);
                            Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, 0);
                            updateList.add(dosFile);
                            continue;
                        }
                        if (null != matMap.get(id)) {
                            List<Aprv_process_materials> list = matMap.get(id);
                            for (Aprv_process_materials material : list) {
                                Ha_attachment file = material.getFile();
                                // 排除已经存在的文件
                                if (matIds.contains(file.getId())) {
                                    continue;
                                }
                                // 3. 生成一个文件项
                                // 3.1 查询最大的顺序号
                                Integer sort = this.dao.queryMaxSotrByParentid(dosTerm.getId());
                                Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, sort == null ? 0 : sort + 1);
                                updateList.add(dosFile);
                            }
                        }
                    }
                }
            }
        }
        // 3. 保存需要更新的材料
        if (!CollectionUtils.isEmpty(updateList)) {
            for (Apl_dossier updateData : updateList) {
                this.saveOrUpdate(updateData);
            }
            log.info("更新文件{}", updateList);
        }

        // 4、更新目录页码结构
        List<Apl_dossier> dossierList = this.updateTreeIndex(applyid);
        // 5、更新的归档主记录的电子卷宗卷数和册数
        this.updateMainRecordIndex(applyid, dossierList);

    }

    /**
     * 构建材料项 (含文件项)
     *
     * @param dosVolume
     * @param termList
     * @param matMap
     * @return
     */
    private List<Apl_dossier> buildTrem(Apl_dossier dosVolume, List<Term> termList, Map<String, List<Aprv_process_materials>> matMap, Integer serviceType) {
        // 2. 需要返回保存的记录
        List<Apl_dossier> result = new ArrayList<>(40);
        int i = 0;
        for (Term term : termList) {
            if (null != term.getMattype()) {
                // 2.1 生成一个材料项
                Apl_dossier dosTerm = new Apl_dossier().BuilderTerm(dosVolume.getApplyid(), term.getName(), dosVolume.getId(), term.getMattype(), i++);
                result.add(dosTerm);
                // 2.2 添加已有分类下的材料
                if (null != dosTerm.getMattype()) {
                    String[] mattypeIds = dosTerm.getMattype().split(",");
                    for (String id : mattypeIds) {
                        // 如果是卷首, 卷目录, 卷尾调用一下生成材料的方法
                        if (id.equals(Tp_mattype.COVER + "")) {
                            // 行政复议
                            Ha_attachment file = null;
                            try {
                                file = this.businessApproveService.createArchiveCover(dosVolume.getApplyid(), serviceType);
                            } catch (Exception e) {
                                log.error("生成卷首文件失败", e);
                                throw new RuntimeException("生成卷首文件失败");
                            }
                            if (ObjectUtil.isNotNull(file)) {
                                // 3. 生成一个文件项 - 卷首
                                Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, 0);
                                result.add(dosFile);
                            }
                            continue;
                        } else if (id.equals(Tp_mattype.DIRECTORY + "")) {
                            Ha_attachment file = this.businessApproveService.createArchiveDirectory(dosVolume.getApplyid(), serviceType);
                            if (ObjectUtil.isNotNull(file)) {
                                // 3. 生成一个文件项 - 卷目录
                                Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, 0);
                                result.add(dosFile);
                            }
                            continue;
                        } else if (id.equals(Tp_mattype.FILINGS + "")) {
                            Ha_attachment file = this.businessApproveService.createArchiveBackCover(dosVolume.getApplyid());
                            if (ObjectUtil.isNotNull(file)) {
                                // 4. 生成一个文件项 - 卷生成一个文件项 - 行政复议案件备考表（封底）
                                Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, 0);
                                result.add(dosFile);
                            }
                            continue;
                        }
                        if (null != matMap.get(id)) {
                            List<Aprv_process_materials> list = matMap.get(id);
                            int j = 0;
                            if (CollectionUtil.isNotEmpty(list)) {
                                list = list.stream().filter(el -> ObjectUtil.isNotNull(el.getFile())).collect(Collectors.toList());
                                for (Aprv_process_materials material : list) {
                                    Ha_attachment file = material.getFile();
                                    // 3. 生成一个文件项
                                    Apl_dossier dosFile = new Apl_dossier().BuilderFile(dosTerm.getApplyid(), file.getFilename(), dosTerm.getId(), file.getId(), id, j++);
                                    result.add(dosFile);
                                }
                            }
                        }
                    }
                }
            }
        }

        return result;
    }


    /**
     * 查询归档所需材料
     *
     * @param applyid
     */
    public Map<String, List<Aprv_process_materials>> queryMaterial(String applyid) {
        Map<String, List<Aprv_process_materials>> result = new HashMap<>(16);
        List<Aprv_process_materials> templist;
        // 1. 查询所有的过程材料
        {
            List<Aprv_process_materials> matlist = this.aprv_process_materialsService.queryAttachByApplyId(applyid);
            if (!CollectionUtils.isEmpty(matlist)) {
                result = matlist.stream().filter(el -> ObjectUtil.isNotEmpty(el.getMattype())).collect(Collectors.groupingBy(material -> material.getMattype().toString()));
            }
        }
        // 2. 查询其他材料
        {
            List<Map<String, Object>> othList = this.std_matter_attachDao.queryDossierMaterial(applyid, Arrays.asList(Tp_mattype.ADMOTHER, Tp_mattype.APPOTHER, Tp_mattype.THIOTHER));
            if (!CollectionUtils.isEmpty(othList)) {
                for (Map<String, Object> v : othList) {
                    // 其它材料
                    Ha_attachment attObj = new Ha_attachment();
                    attObj.setId((String) v.get("attachmentid"));
                    attObj.setFilename((String) v.get("filename"));
                    Integer mattype = (Integer) v.get("mattype");
                    Aprv_process_materials matObj = Aprv_process_materials.builder().attachid(attObj.getId()).file(attObj).mattype(mattype).build();
                    if (null != result.get(mattype.toString())) {
                        templist = result.get(mattype.toString());
                    } else {
                        templist = new ArrayList<>(10);
                    }
                    templist.add(matObj);
                    result.put(mattype.toString(), templist);
                }
            }

        }

        // 3. 查询特殊材料
        {
            // 2.1 查询人员信息
            List<Map<String, Object>> spelist = this.dao.querySpecialMaterial(applyid);
            if (!CollectionUtils.isEmpty(spelist)) {
                // 申请人第三idList
                List<String> appIds = new ArrayList<>(10);
                List<String> thiIds = new ArrayList<>(10);
                for (Map<String, Object> v : spelist) {
                    if ("apl_applicant".equals(v.get("pretype"))) {
                        appIds.add((String) v.get("id"));
                    } else if ("apl_thirdper".equals(v.get("pretype"))) {
                        thiIds.add((String) v.get("id"));
                    }
                }

                // 存储所有的附件id
                List<String> ids = new ArrayList<>(10);
                // 申请人附件id
                List<String> appMatList = new ArrayList<>(10);
                // 第三人id附件id
                List<String> thiMatList = new ArrayList<>(10);
                // 2.2 按表名进行分组
                for (Map<String, Object> v : spelist) {
                    // 所有附件id拼接
                    StringBuffer fileids = new StringBuffer();
                    if (null != v.get("filecard")) {
                        fileids.append(fileids.length() > 0 ? "," + v.get("filecard") : v.get("filecard"));
                    }
                    if (null != v.get("doccard")) {
                        fileids.append(fileids.length() > 0 ? "," + v.get("doccard") : v.get("doccard"));
                    }
                    if (null != v.get("fileentrust")) {
                        fileids.append(fileids.length() > 0 ? "," + v.get("fileentrust") : v.get("fileentrust"));
                    }
                    if (StringUtil.isEmpty(fileids.toString())) {
                        continue;
                    }
                    String[] list = fileids.toString().split(",");

                    // 材料属于申请人还是第三人
                    boolean isapp;
                    if ("apl_applicant".equals(v.get("pretype"))) {
                        isapp = true;
                    } else if ("apl_thirdper".equals(v.get("pretype"))) {
                        isapp = false;
                    } else if ("apl_agent".equals(v.get("pretype"))) {
                        // 如果是代理人的材料, 根据代理的是申请人还是第三人决定
                        String applicantname = (String) v.get("applicantname");
                        List<String> applicants = new ArrayList<String>();
                        if (StringUtil.isEmpty(applicantname)) {
                            log.error("代理人未选择申请人或第三人");
                        } else {
                            applicants = Arrays.asList(applicantname.split(","));
                        }
                        if (appIds.stream().anyMatch(applicants::contains)) {
                            isapp = true;
                        } else if (thiIds.stream().anyMatch(applicants::contains)) {
                            isapp = false;
                        } else {
                            throw new RuntimeException("代理人所代理的人员未找到");
                        }
                    } else {
                        throw new RuntimeException("未知的人员表类型");
                    }
                    for (String s : list) {
                        if (isapp) {
                            appMatList.add(s);
                        } else {
                            thiMatList.add(s);
                        }
                        ids.add(s);
                    }
                }
                // 2.3 去查附件id关联附件表查询附件信息
                if (!CollectionUtils.isEmpty(ids)) {
                    List<Ha_attachment> attlist = ha_attachmentDao.findByIds(ids);
                    for (Ha_attachment v : attlist) {
                        Integer mattype;
                        if (appMatList.contains(v.getId())) {
                            mattype = Tp_mattype.APPSPECIAL;
                        } else if (thiMatList.contains(v.getId())) {
                            mattype = Tp_mattype.THISPECIAL;
                        } else {
                            throw new RuntimeException("未知的附件id");
                        }
                        // 创建自定义的过程材料对象
                        Aprv_process_materials matObj = Aprv_process_materials.builder().attachid(v.getId()).file(v).mattype(mattype).build();
                        if (null != result.get(mattype.toString())) {
                            templist = result.get(mattype.toString());
                        } else {
                            templist = new ArrayList<>(10);
                        }
                        templist.add(matObj);
                        result.put(mattype.toString(), templist);
                    }
                }
            }
        }
        return result;
    }


    /**
     * 重新设置树的页码结构
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Apl_dossier> updateTreeIndex(String applyid) {
        List<Apl_dossier> dossierList = this.getDocTreeListByApplyid(applyid);
        this.read(dossierList);
        this.resetPaperIndexOrderTree(dossierList);
        // 更新的归档主记录的电子卷宗卷数和册数
        this.updateMainRecordIndex(applyid, dossierList);
        return dossierList;
    }

    /**
     * 案件归档接口
     *
     * @param applyid
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void recordCase(String applyid, String userId, String userName, Integer serviceType) {

        Apl_main apl_main = this.apl_mainService.getById(applyid);

        // 1. 关闭所有在办子流程
        ResponseDTO res = this.iPersonalFeign.terminationSonProcess(apl_main.getInstid(), "", userId, "申请已归档，故中途办结");
        if (0 != res.getCode()) {
            log.error("recordCase 关闭所有在办子流程失败 ->  msg[{}]", res.getMsg());
            throw new RuntimeException("关闭所有在办子流程失败");
        }
        // 2. 获取需要修改业务状态的实例id
        List<String> instids = (List<String>) res.getData();
        if (!CollectionUtils.isEmpty(instids)) {
            this.apl_mainService.updateStatusByInstid(St_apply.FINISH, instids);
        }

        // 3. 案件归档
        Apl_archive archive = this.apl_archiveService.getById(applyid);
        archive.setLoguserid(userId);
        archive.setLogusername(userName);
        archive.setLogtime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
        if (serviceType.equals(St_servicetType.REVIEW)) {
            Apl_admreview admreview = this.apl_admreviewService.getById(applyid);
            // 更新归档状态
            admreview.setStatus(St_admreview.ARCHIVE);
            this.apl_admreviewService.saveOrUpdate(admreview);
            // 更新归档状态
            apl_main.setStatus(St_apply.ARCHIVE);
            apl_main.setArchivetime(archive.getLogtime());
            this.apl_mainService.saveOrUpdate(apl_main);
            // 更新电子卷宗
            this.updateDossier(applyid, St_servicetType.REVIEW);
        } else if (serviceType.equals(St_servicetType.SUPERVISE)) {
            // 更新归档状态
            apl_main.setStatus(St_apply.ARCHIVE);
            apl_main.setArchivetime(archive.getLogtime());
            this.apl_mainService.saveOrUpdate(apl_main);
            // 更新电子卷宗
            this.updateDossier(applyid, St_servicetType.SUPERVISE);
        }
    }


    /**
     * 读取文件的页数
     *
     * @param dossier
     * @return
     */
    private synchronized Apl_dossier  readIndex(Apl_dossier dossier) {
        try {
            // 读取pdf的页数
            Ha_attachment file = dossier.getFile();
            if (ObjectUtil.isNotNull(file)) {
                String attachId = file.getId();
                // 判断是否在缓存中
                if (ObjectUtil.isNotNull(cacheHelper.findFileIndex(attachId))) {
//                        log.info("redis缓存中读取文件：" + attachId+ "---对应页数为" + cacheHelper.findFileIndex(attachId));
                    dossier.setTotalNum(cacheHelper.findFileIndex(attachId));
                    return dossier;
                }
                String fileUrl = file.getPath();
                String fileName = file.getFilename();
                if (fileName.lastIndexOf(".") > -1 && (fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".doc") ||
                        fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".docx") ||
                        fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".pdf")
                )) {
                    File tempFile = File.createTempFile(NanoIdUtils.randomNanoId(), ".pdf");
//                        log.info("生成临时文件：" + tempFile.getAbsoluteFile() + "-" + tempFile.getName());
                    FileOutputStream fos = null;
                    InputStream is = null;
                    PDDocument pdDocument = null;
                    try {
                        byte[] pdfByte = new byte[1024];
                        if (fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".doc") ||
                                fileName.substring(fileName.lastIndexOf(".")).equalsIgnoreCase(".docx")) {
                            try {
                                is = RemoteFileUtil.getFileInputStream(fileUrl);
                                if (ObjectUtil.isNotNull(is)) {
                                    pdfByte = PdfConvertUtil.word2pdfWithFont(is);
                                }
                            } catch (Exception e) {
                                throw e;
                            } finally {
                                is.close();
                            }
                        } else {
                            pdfByte = RemoteFileUtil.getFile(fileUrl);
                        }
                        fos = new FileOutputStream(tempFile);
                        // 将pdf内容放到数组当中
                        fos.write(pdfByte);
                        pdDocument = PDDocument.load(tempFile);
                        int pages = pdDocument.getNumberOfPages();
                        pdDocument.close();
                        dossier.setTotalNum(pages);
//                            log.warn("当前文件:" + file.getFilename() + "-页数:" + pages);
                        // 写入缓存
                        cacheHelper.setFileIndex(attachId, pages);
//                            log.warn("写入redis缓存，文件id:" + attachId + "---页数:" + pages);
                    } catch (Exception e) {
                        throw e;
                    } finally {
                        // 关闭文件流
                        fos.close();
                        pdDocument.close();
                        tempFile.deleteOnExit();
//                            log.info("临时文件地址" + tempFile.getPath());
//                            log.info("删除临时文件结果:" + tempFile.delete());
                    }
                }
            }
//                log.info("执行当前任务的线程：" + Thread.currentThread().getName());
        } catch (IOException e) {
            e.printStackTrace();
            dossier.setTotalNum(0);
        } catch (Exception e) {
            e.printStackTrace();
            dossier.setTotalNum(0);
            throw e;
        }
        return dossier;
    }

    /**
     * 读取pdf文件 页码
     */
    public void read(List<Apl_dossier> dossierList) {
        if (CollectionUtil.isNotEmpty(dossierList)) {
            CountDownLatch latch = new CountDownLatch(dossierList.size());
            for (int index = 0; index < dossierList.size(); index++) {
                int finalIndex = index;
                Apl_dossier dossier = dossierList.get(index);
                // 线程读取
                Runnable runner = () -> {
                    try {
                        Thread.currentThread().setName("thread-pool-key:" + dossier.getId());
                        dossierList.set(finalIndex, this.readIndex(dossier));
                    } catch (Exception e) {
                        e.printStackTrace();
                        log.error("线程执行异常:" + Thread.currentThread().getName());
                    } finally {
                        latch.countDown();
                    }
                };
                threadPool.execute(runner);
            }
            try {
                // 等待子线程结束
                latch.await();
                Thread.sleep(500);
            } catch (InterruptedException e) {
                log.error("线程 latch await" + e.getMessage());
                Thread.currentThread().interrupt();
            } finally {
            }
        }
    }


    /**
     * 树结构按照sort排序，由小到大
     *
     * @param result
     */
    private void sortTree(List<Map<String, Object>> result) {
        if (CollectionUtil.isNotEmpty(result)) {
            result = result.stream().sorted((o1, o2) -> {
                if (Integer.parseInt(o1.get("sort").toString()) - Integer.parseInt(o2.get("sort").toString()) > 0) {
                    return 1;
                } else if (Integer.parseInt(o1.get("sort").toString()) - Integer.parseInt(o2.get("sort").toString()) < 0) {
                    return -1;
                } else {
                    return 0;
                }
            }).collect(Collectors.toList());
            result.forEach(item -> {
                List<Map<String, Object>> child = (List<Map<String, Object>>) item.get("children");
                if (CollectionUtil.isNotEmpty(child)) {
                    this.sortTree(child);
                }
            });
        }
    }

    /**
     * 用于增删改后返回对应的树节点,前端node节点的默认值
     *
     * @param dossier
     */
    private void inertTreeVal(Apl_dossier dossier) {
        if (ObjectUtil.isNotEmpty(dossier)) {
            dossier.setKey(dossier.getId());
            dossier.setTitle(dossier.getTitle());
        }
    }

    ;

    /**
     * 对无法序列化的字段赋值-用于前端返回展示
     *
     * @param result
     * @param dossierMap
     */
    private void insertTransientVal(List<Map<String, Object>> result, Map<String, Apl_dossier> dossierMap) {
        if (CollectionUtil.isNotEmpty(result)) {
            result.stream().forEach(item -> {
                if (dossierMap.containsKey(item.get("id"))) {
                    Apl_dossier apl_dossier = dossierMap.get(item.get("id"));
                    item.put("key", apl_dossier.getId());
                    item.put("title", apl_dossier.getName());
                    item.put("startindex", apl_dossier.getStartindex());
                    item.put("endindex", apl_dossier.getEndindex());
                    if (ObjectUtil.isNotNull(apl_dossier.getFile())) {
                        item.put("file", apl_dossier.getFile());
                    }
                }
                List<Map<String, Object>> child = (List<Map<String, Object>>) item.get("children");
                if (CollectionUtil.isNotEmpty(child)) {
                    this.insertTransientVal(child, dossierMap);
                }
            });
        }
    }

    /**
     * 格式化树结构
     *
     * @param dossierList
     * @return
     */
    @Override
    public List<Map<String, Object>> formateTreeStruct(List<Apl_dossier> dossierList) {
        if (CollectionUtil.isNotEmpty(dossierList)) {
            List<Map<String, Object>> mapList = new ArrayList<>();
            dossierList.stream().forEach(item -> {
                mapList.add(BeanUtil.beanToMap(item));
            });
            List<Map<String, Object>> result = CommonUtils.createTree(mapList, "id", "parentid", false, null);
            // 对非序列化的字段进行赋值
            Map<String, Apl_dossier> dossierMap =
                    dossierList.stream().collect(Collectors.toMap(Apl_dossier::getId, each -> each, (value1, value2) -> value1));
            // 按照对应的排序
            insertTransientVal(result, dossierMap);
            sortTree(result);
            return result;
        }
        return Collections.emptyList();
    }


    /**
     * 设置目录的起始和结束页数
     *
     * @param dossierList
     */
    @Transactional(rollbackFor = Exception.class)
    public void resetPaperIndexOrderTree(List<Apl_dossier> dossierList) {
        if (CollectionUtil.isNotEmpty(dossierList)) {
            List<Map<String, Object>> mapList = new ArrayList<>();
            dossierList.stream().forEach(item -> {
                mapList.add(BeanUtil.beanToMap(item));
            });
            List<Map<String, Object>> treeList = CommonUtils.createTree(mapList, "id", "parentid", false, null);
            sortTree(treeList);
            List<Apl_dossier> updateList = new ArrayList<>();
            this.setPagerIndex(0, treeList, updateList);
            // 更新字段
            if (CollectionUtil.isNotEmpty(updateList)) {
                updateList.forEach(item -> {
                    this.saveOrUpdate(item);
                });
            }
        }
    }

    /**
     * 获取需要设置目录的数据
     *
     * @param begin
     * @param treeList
     * @param dossierList
     */
    private int setPagerIndex(Integer begin, List<Map<String, Object>> treeList, List<Apl_dossier> dossierList) {
        if (CollectionUtil.isNotEmpty(treeList)) {
            Integer localBegin = begin;
            for (Map<String, Object> item : treeList) {
                if (ObjectUtil.isNotNull(item.get("children"))) {
                    List<Map<String, Object>> childList = (List<Map<String, Object>>) item.get("children");
                    if (CollectionUtil.isNotEmpty(childList)) {
                        begin = this.setPagerIndex(begin, childList, dossierList);
                        // 过滤子层级数据
                        List<Map<String, Object>> filterChildList = childList.stream().filter(child -> ObjectUtil.isNotNull(child.get("startindex")) && ObjectUtil.isNotNull(child.get(
                                "endindex"))).collect(Collectors.toList());
                        if (CollectionUtil.isNotEmpty(filterChildList)) {
                            item.put("startindex", filterChildList.get(0).get("startindex"));
                            item.put("endindex", filterChildList.get(filterChildList.size() - 1).get("endindex"));
                            Apl_dossier dossier = BeanUtil.toBean(item, Apl_dossier.class);
                            // id 无法通过toBean设置
                            dossier.setId(item.get("id").toString());
                            dossierList.add(dossier);
                        }
                    }
                }
                if (ObjectUtil.isNotNull(item.get("totalNum")) && Integer.parseInt(item.get("totalNum").toString()) != 0) {
                    Integer totalNum = Integer.parseInt(item.get("totalNum").toString());
                    item.put("startindex", begin + 1);
                    begin = begin + totalNum;
                    item.put("endindex", begin);
                    Apl_dossier dossier = BeanUtil.toBean(item, Apl_dossier.class);
                    // id 无法通过toBean设置
                    dossier.setId(item.get("id").toString());
                    dossierList.add(dossier);
                }
            }
            return begin;
        }
        return begin;
    }


    /**
     * 通过卷的parentid查询全部卷下的文件
     *
     * @param dossierList
     * @param parentId    卷parentid
     * @return
     */
    private List<Apl_dossier> getDossierFileList(List<Apl_dossier> dossierList, String parentId) {
        List<Apl_dossier> secondList = dossierList.stream().filter(item -> item.getParentid().equals(parentId)).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(secondList)) {
            List<String> parentList = secondList.stream().map(Apl_dossier::getId).collect(Collectors.toList());
            return dossierList.stream().filter(item -> parentList.contains(item.getParentid())).collect(Collectors.toList());
        }
        return Collections.emptyList();
    }

    /**
     * 更新主记录的电子卷宗页签和页码
     *
     * @param applyid
     * @param dossierList
     */
    public void updateMainRecordIndex(String applyid, List<Apl_dossier> dossierList) {
        Apl_archive apl_archive = apl_archiveService.getById(applyid);
        if (ObjectUtil.isNotNull(apl_archive) && CollectionUtil.isNotEmpty(dossierList)) {
            // 正卷
            Apl_dossier mainDossier = dossierList.stream().filter(item -> item.getType().equals(Tp_dostype.VOLUME) && item.getName().
                    equals("正卷")).findFirst().orElse(null);
            // 副卷
            Apl_dossier subDossier = dossierList.stream().filter(item -> item.getType().equals(Tp_dostype.VOLUME) && item.getName().
                    equals("副卷")).findFirst().orElse(null);
            // 其他卷
            List<Apl_dossier> otherDossier = dossierList.stream().filter(item -> item.getType().equals(Tp_dostype.VOLUME) && (!item.getName().
                    equals("正卷") && !item.getName().equals("副卷"))).collect(Collectors.toList());

            List<Apl_dossier> mainList = ObjectUtil.isNotNull(mainDossier) ? getDossierFileList(dossierList,
                    mainDossier.getId()) : Collections.emptyList();
            List<Apl_dossier> subList = ObjectUtil.isNotNull(subDossier) ? getDossierFileList(dossierList,
                    subDossier.getId()) : Collections.emptyList();
            List<Apl_dossier> otherList = new ArrayList<>();
            if (CollectionUtil.isNotEmpty(otherDossier)) {
                otherDossier.stream().forEach(item -> {
                    List<Apl_dossier> otherTempList = ObjectUtil.isNotNull(item) ? getDossierFileList(dossierList,
                            item.getId()) : Collections.emptyList();
                    if (CollectionUtil.isNotEmpty(otherTempList)) {
                        otherList.addAll(otherTempList);
                    }
                });
            }
            if (CollectionUtil.isNotEmpty(mainList)) {
                apl_archive.setElepositivebook(mainList.size());
                long sum = mainList.stream().filter(item -> ObjectUtil.isNotNull(item.getTotalNum())).mapToInt(Apl_dossier::getTotalNum).sum();
                apl_archive.setElepositivepage((int) sum);
            } else {
                apl_archive.setElepositivebook(0);
                apl_archive.setElepositivepage(0);
            }
            if (CollectionUtil.isNotEmpty(subList)) {
                apl_archive.setElereversebook(subList.size());
                long sum = subList.stream().filter(item -> ObjectUtil.isNotNull(item.getTotalNum())).mapToInt(Apl_dossier::getTotalNum).sum();
                apl_archive.setElereversepage((int) sum);
            } else {
                apl_archive.setElereversebook(0);
                apl_archive.setElereversepage(0);
            }
            if (CollectionUtil.isNotEmpty(otherList)) {
                apl_archive.setEleotherbook(otherList.size());
                long sum = otherList.stream().filter(item -> ObjectUtil.isNotNull(item.getTotalNum())).mapToInt(Apl_dossier::getTotalNum).sum();
                apl_archive.setEleotherpage((int) sum);
            } else {
                apl_archive.setEleotherbook(0);
                apl_archive.setEleotherpage(0);
            }
            this.apl_archiveService.saveOrUpdate(apl_archive);
        }
    }

    /**
     * 初始化电子卷宗数据（复议监督）
     * @param applyid
     */
    @Override
    public void buildInitSuperviseDossier(String applyid) {
        // 1. 查询已有的过程材料
        Map<String, List<Aprv_process_materials>> matMap = this.queryMaterial(applyid);

        // 2. 构建初始化电子卷宗数据结构
        List<Apl_dossier> result = new ArrayList<>(40);
        // 2.1 获取正卷
        List<Volume> volList = new Volume().getList();
        // 2.2 获取正卷下的材料项
        List<Term> termList = new Term().getSuperviseList();
        int i = 0;
        // 正卷
        for (Volume volume : volList) {
            // 2.3 构造一个正卷
            Apl_dossier dosVolume = new Apl_dossier().BuilderVolume(applyid, volume.getName(), i++);
            result.add(dosVolume);
            // 默认的材料项加到第一个正卷下
            if (i == 1) {
                List<Apl_dossier> buildList = this.buildTrem(dosVolume, termList, matMap, St_servicetType.SUPERVISE);
                result.addAll(buildList);
            }
        }
        // 特殊环节的材料 生成对应的数据
        // 申请人材料（包含当前录入材料及人员材料）
        List<Apl_matter_attach> attachList = this.apl_matter_attachService.queryListAndFileByApplyid(applyid);
//        //  申请人材料
//        Term applyTerm = termList.stream().filter(el -> el.getMattype().equals(Integer.toString(Tp_mattype.APPSPECIAL))).findAny().orElse(null);
//        // 申请人材料项
//        Apl_dossier applyDosTerm = new Apl_dossier().BuilderTerm(mainDosVolume.getApplyid(), applyTerm.getName(), mainDosVolume.getId(), applyTerm.getMattype(), i++);
        // 申请人材料项
        Apl_dossier applyDosTerm = result.stream().filter(el -> ObjectUtil.isNotNull(el.getMattype()) && el.getType().equals(Tp_dostype.TERM) &&
                el.getMattype().equals(Integer.toString(Tp_mattype.APPSPECIAL))).findAny().orElse(null);
        if (CollUtil.isNotEmpty(attachList) && ObjectUtil.isNotNull(applyDosTerm)) {
            AtomicReference<Integer> index = new AtomicReference<>(0);
            attachList.stream().forEach((attach -> {
                List<Map> fileList = attach.getFileList();
                if (CollUtil.isNotEmpty(fileList)) {
                    fileList.stream().forEach(file -> {
                        // 生成一个文件项
                        Apl_dossier dosFile = new Apl_dossier().BuilderFile(applyid, file.get("name").toString(), applyDosTerm.getId(), file.get("attachmentid").toString(), null, index.getAndSet(index.get() + 1));
                        result.add(dosFile);
                    });
                }
            }));
        }
        // 处理反馈材料
        List<Apl_feedbackdoc> feedbackdocList = this.apl_feedbackdocService.queryFeedBackDocList(applyid);
        // 处理反馈材料项
//        Term feedbackTerm = termList.stream().filter(el -> el.getMattype().equals(Integer.toString(Tp_mattype.RECEIPT))).findAny().orElse(null);

        // 申请人材料项
        Apl_dossier feedbackDosTerm = result.stream().filter(el ->  ObjectUtil.isNotNull(el.getMattype()) && el.getType().equals(Tp_dostype.TERM) &&
                el.getMattype().equals(Integer.toString(Tp_mattype.RECEIPT))).findAny().orElse(null);
//        Apl_dossier feedbackDosTerm = new Apl_dossier().BuilderTerm(mainDosVolume.getApplyid(), feedbackTerm.getName(), mainDosVolume.getId(), feedbackTerm.getMattype(), i++);
//        result.add(feedbackDosTerm);
        if (CollUtil.isNotEmpty(feedbackdocList) && ObjectUtil.isNotNull(feedbackDosTerm)) {
            AtomicReference<Integer> index = new AtomicReference<>(0);
            feedbackdocList.stream().forEach((feedBack -> {
                List<Ha_attachment> fileList = feedBack.getAttachmentList();
                if (CollUtil.isNotEmpty(fileList)) {
                    fileList.stream().forEach(file -> {
                        // 生成一个文件项
                        Apl_dossier dosFile = new Apl_dossier().BuilderFile(applyid, file.getFilename(), feedbackDosTerm.getId(), file.getId(), null, index.getAndSet(index.get() + 1));
                        result.add(dosFile);
                    });
                }
            }));
        }
        // 删除之前环节生成的卷宗表数据，删除完成后再插入新的数据
        this.dao.removeByApplyid(applyid);
        this.saveAll(result);
        log.info("保存文件{}", result);

        // 3. 更新目录页码结构
        this.updateTreeIndex(applyid);
        // 4.更新的归档主记录的电子卷宗卷数和册数
        this.updateMainRecordIndex(applyid, result);
    }

}
